package red.stu.mc.api;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;

import javax.imageio.ImageIO;

import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityItemFrame;
import net.minecraft.item.Item;
import net.minecraft.item.ItemMap;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.storage.MapData;
import red.stu.mc.mod.codeblock.util.MapColorUtil;

public class MapApi {

	private World worldIn;

	private static final byte[] COLORS_NULL = new byte[16384];
	private static final ItemStack[] ITEM_STACK_NULL = new ItemStack[0];

	public MapApi(World worldIn) {
		this.worldIn = worldIn;
	}

	/**
	 * 根据物品展示柜，修改放置的地图颜色 （此修改不会立即生效，需要重新读数据渲染；则一般在重新载入游戏生效）
	 * 
	 * @param frame
	 * @param colors
	 * @return
	 */
	public boolean setMapColor(EntityItemFrame frame, byte[] colors) {
		ItemStack stack = frame.getDisplayedItem();
		if (stack == null)
			return false;
		Item item = stack.getItem();
		if (item instanceof ItemMap) {
			MapData mapdata = ((ItemMap) item).getMapData(stack, worldIn);
			mapdata.colors = colors;
			mapdata.trackingPosition = false;
			mapdata.unlimitedTracking = false;
			mapdata.markDirty();
			return true;
		}
		return false;
	}
	
	public ItemStack getNewItemMap(byte[] colors) {
		ItemStack stack = ItemMap.setupNewMap(worldIn, 0, 0, (byte) 3,
				false, false);
		// 创建map，设置显示数据
		MapData mapdata = ((ItemMap) stack.getItem()).getMapData(stack, worldIn);
		mapdata.colors = colors;
		mapdata.trackingPosition = false;
		mapdata.unlimitedTracking = false;
		return stack;
	}

	/**
	 * 放置一个新的地图在物品展示柜
	 * 
	 * @param frame
	 * @param colors
	 * @return
	 */
	public ItemStack setItemFrameNewItemMap(EntityItemFrame frame, byte[] colors) {
		ItemStack stack = ItemMap.setupNewMap(worldIn, frame.getPosition().getX(), frame.getPosition().getY(), (byte) 3,
				false, false);
		// 创建map，设置显示数据
		MapData mapdata = ((ItemMap) stack.getItem()).getMapData(stack, worldIn);
		mapdata.colors = colors;
		mapdata.trackingPosition = false;
		mapdata.unlimitedTracking = false;
		frame.setDisplayedItem(stack);
		return stack;
	}

	/**
	 * 设置物品展示柜物品
	 * 
	 * @param frame
	 * @param stack
	 */
	public void setItemFrameItem(EntityItemFrame frame, ItemStack stack) {
		frame.setDisplayedItem(stack);
	}

	/**
	 * 产生一个新的地图
	 * 
	 * @param worldX
	 * @param worldZ
	 * @param scale
	 * @return
	 */
	public ItemStack setupNewMap(double worldX, double worldZ, byte scale) {
		// trackingPosition 跟踪位置 > false
		// unlimitedTracking 无限跟踪 > false
		return ItemMap.setupNewMap(worldIn, worldX, worldZ, scale, false, false);
	}

	/**
	 * 获取当前世界已加载的所有实体
	 * 
	 * @return
	 */
	public List<Entity> getLoadedEntityList() {
		return worldIn.getLoadedEntityList();
	}

	/**
	 * 获取指定范围内的所有物品展示柜
	 * 
	 * @param x1
	 * @param y1
	 * @param z1
	 * @param x2
	 * @param y2
	 * @param z2
	 * @return
	 */
	public EntityItemFrame[] getEntityItemFrameInAABB(double x1, double y1, double z1, double x2, double y2,
			double z2) {
		List<EntityItemFrame> frameList = worldIn.getEntitiesWithinAABB(EntityItemFrame.class,
				new AxisAlignedBB(x1, y1 + 1, z1 + 1, x2 + 1, y2, z2));
		EntityItemFrame[] frames = {};
		frames = frameList.toArray(frames);
		return frames;
	}

	public EntityItemFrame[] getEntityItemFrameInAABBOrderlyNew(double x1, double y1, double z1, double x2, double y2,
			double z2) {
		y1 += 1;
		z1 += 1;
		x2 += 1;
		List<EntityItemFrame> frameList = worldIn.getEntitiesWithinAABB(EntityItemFrame.class,
				new AxisAlignedBB(x1, y1, z1, x2, y2, z2));
		EntityItemFrame[] itemFrames = {};
		itemFrames = frameList.toArray(itemFrames);
		int len = itemFrames.length;
		if (len == 0)
			return new EntityItemFrame[] {};
		EntityItemFrame[][][] pTemps = new EntityItemFrame[len * 2 - 1][len * 2 - 1][len * 2 - 1];
		EPosition startP = new EPosition(itemFrames[0].getPosition());
		int maxIndex = len - 1;
		pTemps[maxIndex][maxIndex][maxIndex] = itemFrames[0];
		EPosition temp = null;
		EPosition min = startP;
		for (int i = 0; i > itemFrames.length; i++) {
			/*
			 * 0 1 2 3
			 * 
			 * a b c 0 a b c
			 * 
			 */
			// ps[len + i] = itemFrames[i];
			temp = new EPosition(itemFrames[i].getPosition());
			int abX = temp.x - startP.x;
			int abY = temp.y - startP.y;
			int abZ = temp.z - startP.z;
			pTemps[maxIndex + abX][maxIndex + abY][maxIndex + abZ] = itemFrames[i];
			// min pos
			min.x = temp.x < min.x ? temp.x : min.x;
			min.y = temp.y < min.y ? temp.y : min.y;
			min.z = temp.z < min.z ? temp.z : min.z;
		}

		return null;
	}

	public EntityItemFrame[] getEntityItemFrameInAABBOrderly(double x1, double y1, double z1, double x2, double y2,
			double z2) {
		y1 += 1;
		z1 += 1;
		x2 += 1;
		List<EntityItemFrame> frameList = worldIn.getEntitiesWithinAABB(EntityItemFrame.class,
				new AxisAlignedBB(x1, y1, z1, x2, y2, z2));
		int[] maxs = { Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE };
		int[] mins = { Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE };
		HashMap<String, EntityItemFrame> entityItemFrameMap = new HashMap<>();
		for (EntityItemFrame frame : frameList) {
			int x = (int) frame.posX, y = (int) (frame.posY + 0.5D), z = (int) frame.posZ;
			maxs[0] = Integer.max(maxs[0], x);
			mins[0] = Integer.min(mins[0], x);
			maxs[1] = Integer.max(maxs[1], y);
			mins[1] = Integer.min(mins[1], y);
			maxs[2] = Integer.max(maxs[2], z);
			mins[2] = Integer.min(mins[2], z);
			entityItemFrameMap.put(x + "_" + y + "_" + z, frame);
		}
		int len1 = maxs[1] - mins[1] + 1; // y 数量
		int len2 = 1; // x或z 数量
		short type = 0;
		if (maxs[0] == mins[0]) {
			len2 = maxs[2] - mins[2] + 1;
			type = 1;
		} else if (maxs[2] == mins[2]) {
			len2 = maxs[0] - mins[0] + 1;
			type = 3;
		}
		int len = len1 * len2;
		EntityItemFrame[] frames = new EntityItemFrame[len];
		int x = mins[0], y, z = mins[2];
		for (int i = 0; i < len1; i++) {
			y = mins[1] + i;
			for (int j = 0; j < len2; j++) {
				if (type == 1) {
					z = mins[2] + j;
				} else if (type == 3) {
					x = mins[0] + j;
				}
				frames[i * len2 + j] = entityItemFrameMap.get(x + "_" + y + "_" + z);
			}
		}
		return frames;
	}

	/**
	 * 获取已经排序后的指定范围内的所有物品展示柜
	 * 
	 * @param x1
	 * @param y1
	 * @param z1
	 * @param x2
	 * @param y2
	 * @param z2
	 * @return
	 */
	public EntityItemFrame[] getEntityItemFrameInAABBOrderly2(double x1, double y1, double z1, double x2, double y2,
			double z2) {
		y1 += 1;
		z1 += 1;
		x2 += 1;
		List<EntityItemFrame> frameList = worldIn.getEntitiesWithinAABB(EntityItemFrame.class,
				new AxisAlignedBB(x1, y1, z1, x2, y2, z2));
		int[] maxs = { Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE };
		int[] mins = { Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE };
		HashMap<String, EntityItemFrame> entityItemFrameMap = new HashMap<>();
		for (EntityItemFrame frame : frameList) {
			int x = (int) frame.posX, y = (int) (frame.posY + 0.5D), z = (int) frame.posZ;
			maxs[0] = Integer.max(maxs[0], x);
			mins[0] = Integer.min(mins[0], x);
			maxs[1] = Integer.max(maxs[1], y);
			mins[1] = Integer.min(mins[1], y);
			maxs[2] = Integer.max(maxs[2], z);
			mins[2] = Integer.min(mins[2], z);
			entityItemFrameMap.put(x + "_" + y + "_" + z, frame);
		}
		int len1 = maxs[1] - mins[1] + 1; // y 数量
		int len2 = 1; // x或z 数量
		short type = 0;
		if (maxs[0] == mins[0]) {
			len2 = maxs[2] - mins[2] + 1;
			type = 1;
		} else if (maxs[2] == mins[2]) {
			len2 = maxs[0] - mins[0] + 1;
			type = 3;
		}
		int len = len1 * len2;
		EntityItemFrame[] frames = new EntityItemFrame[len];
		int x = mins[0], y, z = mins[2];
		for (int i = 0; i < len1; i++) {
			y = mins[1] + i;
			for (int j = 0; j < len2; j++) {
				if (type == 1) {
					z = mins[2] + j;
				} else if (type == 3) {
					x = mins[0] + j;
				}
				frames[(i + 1) * (j + 1)] = entityItemFrameMap.get(x + "_" + y + "_" + z);
			}
		}
		return frames;
	}
	
	/**
	 * 设置物品展示柜图片 - 本机物理路径
	 * @param x1
	 * @param y1
	 * @param z1
	 * @param x2
	 * @param y2
	 * @param z2
	 * @param fileName
	 * @param wSize
	 * @param hSize
	 * @return
	 */
	public ItemStack[] setItemFrameImgFile(double x1, double y1, double z1, double x2, double y2, double z2, String fileName, int wSize, int hSize, boolean wReversal) {
		File file = new File(fileName);
		try {
			return setItemFrameImg(x1, y1, z1, x2, y2, z2, ImageIO.read(file), wSize, hSize, wReversal);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 设置物品展示柜图片 - 网络图片路径
	 * @param x1
	 * @param y1
	 * @param z1
	 * @param x2
	 * @param y2
	 * @param z2
	 * @param fileUrl
	 * @param wSize
	 * @param hSize
	 * @return
	 */
	public ItemStack[] setItemFrameImgUrl(double x1, double y1, double z1, double x2, double y2, double z2, String fileUrl, int wSize, int hSize, boolean wReversal) {
		try {
			URL url = new URL(fileUrl);
			return setItemFrameImg(x1, y1, z1, x2, y2, z2, ImageIO.read(url), wSize, hSize, wReversal);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 设置物品展示柜图片 - 资源路径
	 * @param x1
	 * @param y1
	 * @param z1
	 * @param x2
	 * @param y2
	 * @param z2
	 * @param name 路径名 "/assets/forge/textures/gui/version_check_icons.png"
	 * @param wSize 宽度大小(物品展示柜数量为尺寸)
	 * @param hSize 高度大小(物品展示柜数量为尺寸)
	 * @return
	 */
	public ItemStack[] setItemFrameImgR(double x1, double y1, double z1, double x2, double y2, double z2, String name, int wSize, int hSize, boolean wReversal) {
		URL url = MapApi.class.getResource(name);
		try {
			return setItemFrameImg(x1, y1, z1, x2, y2, z2, ImageIO.read(url), wSize, hSize, wReversal);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 设置物品展示柜的地图图片 <br>
	 * 		必须保证坐标内的物品展示柜数量和wSize*hSize一致，否则将返回false
	 * @param x1
	 * @param y1
	 * @param z1
	 * @param x2
	 * @param y2
	 * @param z2
	 * @param image
	 * @param wSize
	 * @param hSize
	 * @return
	 */
	public ItemStack[] setItemFrameImg(double x1, double y1, double z1, double x2, double y2, double z2, BufferedImage image, int wSize, int hSize, boolean wReversal) {
		EntityItemFrame[] frames = getEntityItemFrameInAABBOrderly(x1, y1, z1, x2, y2, z2);
		byte[][] colors = new byte[wSize * hSize][];
		if (frames.length != colors.length) {
			return ITEM_STACK_NULL;
		}
		int tempW;
		for (int w = 0; w < wSize; w++) {
			for (int h = 0; h < hSize; h++) {
				if (wReversal)
					tempW = wSize - w - 1;
				else
					tempW = w;
				colors[h * wSize + tempW] = imgToColors(image, w * 128, h * 128);
			}
		}
		int len = frames.length;
		ItemStack[] itemStacks = new ItemStack[len];
		for (int i = 0; i < len; i++) {
			byte[] c = colors[i];
			if (c != null)
				itemStacks[len - i - 1] = setItemFrameNewItemMap(frames[len - i - 1], c);
		}
		return itemStacks;
	}

	/**
	 * 本地图片转MC地图颜色
	 * 
	 * @param fileName
	 *            文件路径
	 * @return
	 */
	public byte[] imgFileToColors(String fileName) {
		return imgFileToColors(fileName, 0, 0);
	}

	/**
	 * 本地图片转MC地图颜色
	 * 
	 * @param fileName
	 * @param dX
	 * @param dY
	 * @return
	 */
	public byte[] imgFileToColors(String fileName, int dX, int dY) {
		File file = new File(fileName);
		try {
			return imgToColors(ImageIO.read(file), dX, dY);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return COLORS_NULL;
	}

	/**
	 * 网络图片转MC地图颜色
	 * 
	 * @param fileUrl
	 * @return
	 */
	public byte[] imgUrlToColors(String fileUrl) {
		return imgUrlToColors(fileUrl, 0, 0);
	}

	/**
	 * 网络图片转MC地图颜色
	 * 
	 * @param fileUrl
	 * @param dX
	 * @param dY
	 * @return
	 */
	public byte[] imgUrlToColors(String fileUrl, int dX, int dY) {
		try {
			URL url = new URL(fileUrl);
			return imgToColors(ImageIO.read(url), dX, dY);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return COLORS_NULL;
	}

	public byte[] imgToColors(BufferedImage image, int dX, int dY) {
		byte[] colors = new byte[16384];
		int width = image.getWidth();
		int height = image.getHeight();
		for (int i = 0; i < 16384; i++) {
			int x = i % 128 + dX;
			int y = i / 128 + dY;
			try {
				colors[i] = (byte) MapColorUtil.getMCColor(image.getRGB(x, y));
			} catch (Exception e) {
				colors[i] = 0;
			}
		}
		image.flush();
		return colors;
	}

	public String testShowEntityList() {
		Entity[] es = {};
		es = getLoadedEntityList().toArray(es);
		return testShowEntityList(es);
	}

	public String testShowEntityList(Entity[] es) {
		StringBuffer sb = new StringBuffer();
		for (Entity e : es) {
			sb.append(e.toString() + "\n");
		}
		return sb.toString();
	}

	public byte[] testColor(int color) {
		byte[] colors = new byte[16384];
		for (int i = 0; i < 16384; i++) {
			colors[i] = (byte) color;
		}
		return colors;
	}

	public byte[] testColor2() {
		byte[] colors = new byte[16384];
		File file = new File("C:\\Users\\ASUS\\Desktop\\123.jpg");
		try {
			BufferedImage image = ImageIO.read(file);
			int width = image.getWidth();
			int height = image.getHeight();
			for (int i = 0; i < 128; i++) {
				for (int j = 0; j < 128; j++) {
					colors[(i + 1) * (j + 1) - 1] = (byte) MapColorUtil.getMCColor(image.getRGB(i, j));
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return colors;
	}

	public byte[] testColort2() {
		byte[] colors = new byte[16384];
		File file = new File("C:\\Users\\ASUS\\Desktop\\123.jpg");
		try {
			BufferedImage image = ImageIO.read(file);
			int width = image.getWidth();
			int height = image.getHeight();
			for (int i = 0; i < 16384; i++) {
				int x = i % 128;
				int y = i / 128;
				colors[i] = (byte) MapColorUtil.getMCColor(image.getRGB(x, y));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return colors;
	}

	public byte[] testColor3() {
		byte[] colors = new byte[16384];
		int index = 0;
		for (int i = 0; i < 16384; i++) {
			if (++index > 51)
				index = 0;
			colors[i] = (byte) index;
		}
		return colors;
	}

	public byte[] testColor4() {
		byte[] colors = new byte[16384];
		for (int i = 0; i < 51; i++) {
			colors[i + 10] = (byte) i;
		}
		return colors;
	}

	public byte[] testColor5() {
		byte[] colors = new byte[16384];
		int index = 0;
		for (int i = 0; i < 16384; i++) {
			if (i % 127 == 0)
				index++;
			colors[i] = (byte) index;
		}
		return colors;
	}

	private class EPosition {

		private int x;

		private int y;

		private int z;

		EPosition(BlockPos blockPos) {
			x = blockPos.getX();
			y = blockPos.getY();
			z = blockPos.getZ();
		}

	}

}
